package cn.kennylee.codehub.mongodb.das.extension;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.TypeUtil;
import cn.hutool.json.JSONUtil;
import cn.kennylee.codehub.common.das.dto.CustomPageDto;
import cn.kennylee.codehub.common.das.dto.PageDto;
import cn.kennylee.codehub.mongodb.das.entity.MongoDbEo;
import cn.kennylee.codehub.mongodb.das.utils.MongoDbDasHelper;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 抽象Mongo数据访问层，扩展业务特性
 * <p>Created on 2024/12/6.</p>
 *
 * @author kennylee
 * @since 0.0.1
 */
@Slf4j
public abstract class AbstractMongoDas<T extends MongoDbEo<P>, P extends Serializable> extends ComMongoDas<T, P> {

    protected Class<T> entityClass = currentModelClass();

    /**
     * 分页查询时，是否允许磁盘使用
     */
    @Setter
    protected boolean findPageAllowDiskUse;

    /**
     * 分页查询
     *
     * @param pageExample 分页查询参数
     * @param <E>         分页查询参数
     * @return CustomPage 分页结果
     */
    @SuppressWarnings("unchecked")
    public <E extends PageDto> CustomPageDto<T> findPage(E pageExample) {
        return (CustomPageDto<T>) findPage(pageExample, entityClass);
    }

    /**
     * 分页查询
     *
     * @param pageExample          分页查询参数
     * @param extQueryConsumerFunc 自定义查询条件逻辑
     * @param <E>                  分页查询参数
     * @return CustomPage 分页结果
     */
    @SuppressWarnings("unchecked")
    public <E extends PageDto> CustomPageDto<T> findPage(E pageExample, @Nullable ExtQueryConsumerFunc extQueryConsumerFunc) {
        return (CustomPageDto<T>) findPage(pageExample, entityClass, extQueryConsumerFunc);
    }

    /**
     * 分页查询
     *
     * @param pageExample          分页查询参数
     * @param extQueryConsumerFunc 自定义查询条件逻辑
     * @param <E>                  分页查询参数
     * @return CustomPage 分页结果
     */
    @SuppressWarnings("unchecked")
    public <E extends PageDto> CustomPageDto<T> findPage(E pageExample, @Nullable ExtQueryConsumerFunc extQueryConsumerFunc, Collection<String> ignoreProps) {
        return (CustomPageDto<T>) findPage(pageExample, entityClass, extQueryConsumerFunc, ignoreProps);
    }

    /**
     * 分页查询
     *
     * @param pageExample   分页查询参数
     * @param <E>           分页查询参数
     * @param responseClass 返回结果类
     * @return CustomPage 分页结果
     */
    public <E extends PageDto> CustomPageDto<?> findPage(E pageExample, @Nullable Class<?> responseClass) {
        return findPage(pageExample, responseClass, null);
    }

    /**
     * 分页查询
     *
     * @param pageExample          分页查询参数
     * @param <E>                  分页查询参数
     * @param responseClass        返回结果类
     * @param extQueryConsumerFunc 自定义查询条件逻辑
     * @return CustomPage 分页结果
     */
    public <E extends PageDto> CustomPageDto<?> findPage(E pageExample, @Nullable Class<?> responseClass,
                                                         @Nullable ExtQueryConsumerFunc extQueryConsumerFunc) {
        return findPage(pageExample, responseClass, extQueryConsumerFunc, Collections.emptyList());
    }

    /**
     * 分页查询
     *
     * @param pageExample          分页查询参数
     * @param <E>                  分页查询参数
     * @param responseClass        返回结果类
     * @param extQueryConsumerFunc 自定义查询条件逻辑
     * @param ignoreProps          忽略的属性列表
     * @return CustomPage 分页结果
     */
    public <E extends PageDto> CustomPageDto<?> findPage(E pageExample, @Nullable Class<?> responseClass,
                                                         @Nullable ExtQueryConsumerFunc extQueryConsumerFunc,
                                                         Collection<String> ignoreProps) {

        Query query = MongoDbDasHelper.buildQuery(pageExample, getEnityClass(), ignoreProps);

        // 添加自定义查询条件
        if (Objects.nonNull(extQueryConsumerFunc)) {
            extQueryConsumerFunc.accept(query);
        }

        // -1代表没有count
        long count = -1;
        if (Objects.isNull(pageExample.getSearchCount()) || pageExample.getSearchCount()) {
            count = super.countByQuery(query, entityClass);
            log.debug("总条目数: {}", count);
            if (count == 0) {
                return new CustomPageDto<>(pageExample.getPageNo(), pageExample.getPageSize(), count, CollUtil.newArrayList());
            }
        }
        // mongo从0开始分页
        int pageNo = pageExample.getPageNo() - 1;
        PageRequest pageRequest = PageRequest.of(pageNo, pageExample.getPageSize());
        query.with(pageRequest);

        // 添加排序
        if (MongoDbDasHelper.addSorts(query, pageExample) == 0) {
            MongoDbDasHelper.addBaseUniqueSort(query);
        }

        // 如果排序不为空，根据配置设置是否允许磁盘使用
        if (Boolean.FALSE == isEmptySort(query)) {
            if (findPageAllowDiskUse) {
                query.allowDiskUse(true);
            }
        }

        List<T> resultData = CollUtil.emptyIfNull(getMongoTemplate().find(query, entityClass));

        if (log.isDebugEnabled()) {
            log.debug("查询结果: {}", JSONUtil.toJsonStr(resultData));
        }

        List<?> responseList = Objects.nonNull(responseClass) ? BeanUtil.copyToList(resultData, responseClass) : resultData;

        return new CustomPageDto<>(pageExample.getPageNo(), pageExample.getPageSize(), count, responseList);
    }

    @SuppressWarnings("unchecked")
    protected Class<T> currentModelClass() {
        return (Class<T>) TypeUtil.getClass(TypeUtil.getTypeArgument(this.getClass()));
    }

    @Override
    protected Class<T> getEnityClass() {
        return this.entityClass;
    }
}
